/**
 * @license Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';
const childProcess = require('child_process');
const fs = require('fs');
const path = require('path');

const opn = require('opn');
const args = require('yargs')
  .wrap(Math.min(process.stdout.columns, 120))
  .example('node $0 out-parent-folder')
  .example('node $0 out-1 out-2 out-3')
  .help('help')
  .demand(1)
  .argv;

const constants = require('./constants');
const utils = require('./utils');

/**
 * Run analyze.js on each of the outs and then
 * aggregate all the results by metric.
 */
function main() {
  const inputPaths = getInputPaths();
  analyzeInputPaths(inputPaths);
  const results = {};
  for (const inputPath of inputPaths) {
    const result = fs.readFileSync(
      path.resolve(inputPath, constants.GENERATED_RESULTS_FILENAME),
      'utf-8'
    );
    results[path.basename(inputPath)] = /** @type {!ResultsByMetric} */ (JSON.parse(
      result.replace('var generatedResults = ', '')
    ));
  }

  const groupByMetricResults = groupByMetric(results);

  if (!utils.isDir(constants.OUT_PATH)) {
    fs.mkdirSync(constants.OUT_PATH);
  }
  fs.writeFileSync(
    path.resolve(constants.OUT_PATH, 'dashboard-results.js'),
    `const dashboardResults = ${JSON.stringify(groupByMetricResults, undefined, 2)}`
  );

  if (process.env.CI) {
    return;
  }
  // eslint-disable-next-line no-console
  console.log('Opening the dashboard web page...');
  opn(path.resolve(__dirname, 'dashboard', 'index.html'));
}

main();

/**
 *
 * @param {!AggregatedResults} results
 * @return {!GroupByMetricResults}
 */
function groupByMetric(results) {
  return Object.keys(results).reduce(
    (acc, batchId) => {
      const batchResults = results[batchId];
      Object.keys(batchResults).forEach(metricId => {
        if (!acc[metricId]) {
          acc[metricId] = {};
        }
        const sites = batchResults[metricId];
        sites.forEach(site => {
          if (!acc[metricId][site.site]) {
            acc[metricId][site.site] = {};
          }
          acc[metricId][site.site][batchId] = site.metrics;
        });
      });
      return acc;
    },
    {}
  );
}

/**
 * @param {!Array<string>} inputPaths
 */
function analyzeInputPaths(inputPaths) {
  for (const inputPath of inputPaths) {
    // Prevent analyze script from opening the results in browser
    childProcess.execSync(`node analyze.js ${inputPath}`, {
      env: Object.assign({}, process.env, {CI: '1'}),
    });
  }
}

/**
 * Returns a list of out paths generated by measure.js
 * @return {!Array<string>}
 */
function getInputPaths() {
  const relativePaths = args._;
  let inputPaths = [];
  relativePaths.forEach(relativePath => {
    const fullPath = path.resolve(__dirname, relativePath);
    if (isOutParentFolder(fullPath)) {
      const paths = fs.readdirSync(fullPath)
        .map(pathComponent => path.resolve(fullPath, pathComponent))
        .filter(inputPath => utils.isDir(inputPath));
      inputPaths = inputPaths.concat(paths);
      return;
    }
    inputPaths.push(fullPath);
  });
  return inputPaths;
}

/**
 * Detects if any of the directory's children is a valid input path
 * @param {boolean} fullPath
 */
function isOutParentFolder(fullPath) {
  for (const maybeOutFolder of fs.readdirSync(fullPath)) {
    const maybeOutPath = path.resolve(fullPath, maybeOutFolder);
    if (!utils.isDir(maybeOutPath)) {
      continue;
    }
    for (const maybeSiteFolder of fs.readdirSync(maybeOutPath)) {
      const maybeSitePath = path.resolve(maybeOutPath, maybeSiteFolder);
      if (!utils.isDir(maybeSitePath)) {
        continue;
      }
      for (const maybeRunFolder of fs.readdirSync(maybeSitePath)) {
        const maybeLighthouseResults = path.resolve(
          maybeSitePath, maybeRunFolder, constants.LIGHTHOUSE_RESULTS_FILENAME);
        if (utils.isFile(maybeLighthouseResults)) {
          return true;
        }
      }
    }
  }
  return false;
}

/**
 * @typedef {!Object<string, !BatchResultsBySite}
 */
let GroupByMetricResults; // eslint-disable-line no-unused-vars

/**
 * @typedef {!Object<string, !Object<string, !Array<{timing: number}>>>}
 */
let BatchResultsBySite; // eslint-disable-line no-unused-vars
